﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MVCPets.Helpers
{
  public static class PasswordGenerator
  {
    #region Public Members/Enums
    public enum PasswordType
    {
      NoDuplicates,
      DuplicatesAllowed
    }
    #endregion

    #region Public Methods
    public static string GeneratePassword(int PasswordLength = 18, PasswordType PType = PasswordType.NoDuplicates)
    {
      string ret = string.Empty;

      if (PType == PasswordType.DuplicatesAllowed) ret = GetGuidPassword(PasswordLength);
      else ret = GenerateRandomPassword(PasswordLength);

      return ret;
    }
    #endregion

    #region Private Methods
    private static string GetGuidPassword(int Length)
    {
      string ret = string.Empty;

      while (ret.Length < Length) ret += StripMistakableChars(Guid.NewGuid().ToString().Replace("-", string.Empty));

      return ret.Substring(0, Length);
    }

    private static string GenerateRandomPassword(int Length)
    {
      var chars = GetValidCharacters();
      var ret = MixUpString(chars);

      if (Length > chars.Length) throw new PasswordException(string.Format("Length must be less than {0}", chars.Length + 1));

      return ret.Substring(0, Length);
    }

    private static string MixUpString(string Value)
    {
      var chars = Value.ToCharArray();
      char tmp;
      var rand = new Random();

      foreach (var i in Enumerable.Range(0, chars.Length * 5))
      {
        var source = rand.Next(0, chars.Length);
        var dest = rand.Next(0, chars.Length);

        tmp = chars[dest];
        chars[dest] = chars[source];
        chars[source] = tmp;
      }

      return new string(chars);
    }

    private static string GetValidCharacters()
    {
      var sb = new StringBuilder();

      foreach (var i in Enumerable.Range(48, 58 - 48)) sb.Append((char)i);
      foreach (var i in Enumerable.Range(65, 26)) sb.Append((char)i);
      foreach (var i in Enumerable.Range(97, 26)) sb.Append((char)i);

      return StripMistakableChars(sb.ToString());
    }

    private static string StripMistakableChars(string Value)
    {
      Value = Value.Replace("0", string.Empty); // zero
      Value = Value.Replace("o", string.Empty); // lowercase
      Value = Value.Replace("O", string.Empty); // uppercase
      Value = Value.Replace("1", string.Empty); // one
      Value = Value.Replace("l", string.Empty); // lowercase
      Value = Value.Replace("I", string.Empty); // uppercase

      return Value;
    }
    #endregion
  }
}
